package com.xayy.mgt.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.kawins.base.result.CheckResult;
import com.alibaba.fastjson.JSONObject;
import com.xayy.core.constant.ConfigCalPath;
import com.xayy.mgt.model.dto.GenerateTheoryDataDto;
import com.xayy.mgt.model.vo.ConfigFileVo;
import com.xayy.mgt.model.vo.GenerateTheoryDataVo;
import com.xayy.mgt.model.vo.SateRateAndCacheVo;
import com.xayy.mgt.model.xinwangmodel.SateAndLinkData;
import com.xayy.mgt.model.xinwangmodel.SateLinkData;
import com.xayy.mgt.model.xinwangmodel.SateListData;
import com.xayy.mgt.model.xinwangmodel.TopoList;
import com.xayy.mgt.service.GenerateTheoryDataAsyncService;
import com.xayy.mgt.service.GenerateTheoryDataService;
import com.xayy.mgt.service.NodeInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 生成理论数据
 * @author plq
 */
@Service
@Slf4j
public class GenerateTheoryDataServiceImpl implements GenerateTheoryDataService {


    private final ConfigCalPath configCalPath;

    private  final NodeInfoService nodeInfoService;
    private  final GenerateTheoryDataAsyncService gg;

    public GenerateTheoryDataServiceImpl(NodeInfoService nodeInfoService,ConfigCalPath configCalPath,GenerateTheoryDataAsyncService gg) {
        this.nodeInfoService = nodeInfoService;
        this.configCalPath = configCalPath;
        this.gg = gg;
    }

    @Override
    public CheckResult<Object> getConfigFile() {
        String data_ground = FileUtil.readString(configCalPath.getTemplateFiles() + "data_ground.txt",  CharsetUtil.UTF_8);
        String Polar_TLEV = FileUtil.readString(configCalPath.getTemplateFiles()+"Polar_TLEV.txt",  CharsetUtil.UTF_8);
        List<Map<String,Object>> list = new ArrayList<>();
        Map<String,Object> dataGroundMap = new HashMap<>();
        dataGroundMap.put("tabName","地面站配置文件");
        dataGroundMap.put("value",data_ground);
        dataGroundMap.put("fileName","data_ground.txt");
        Map<String,Object> polarTLEVMap = new HashMap<>();
        polarTLEVMap.put("tabName","TLE配置文件");
        polarTLEVMap.put("value",Polar_TLEV);
        polarTLEVMap.put("fileName","Polar_TLEV.txt");
        list.add(dataGroundMap);
        list.add(polarTLEVMap);
        return CheckResult.success(list);
    }


    @Override
    public CheckResult<Object> updateConfigFile(List<ConfigFileVo> configFileVo) {
        try {
            configFileVo.forEach(item -> FileUtil.writeString(item.getValue(), configCalPath.getTemplateFiles() + item.getFileName(), CharsetUtil.UTF_8));

        } catch (IORuntimeException e) {
            return CheckResult.success("文件修改失败");
        }
        return CheckResult.success("文件修改成功");
    }


    @Override
    public CheckResult<Object> execute(GenerateTheoryDataDto dataDto) {

        /*上传groundFile*/
        String groundFileMsg = gg.requestUploadFile("/temp",configCalPath.getTemplateFiles()+"data_ground.txt");
        if(groundFileMsg == null){
            return CheckResult.fail("data_ground.txt文件上传失败");
        }
        /*上传tleFile*/
        String tleFileMsg = gg.requestUploadFile("/temp",configCalPath.getTemplateFiles()+"Polar_TLEV.txt");
        if(tleFileMsg == null){
            return CheckResult.fail("Polar_TLEV.txt文件上传失败");
        }
        /*修改并且上传py文件*/
        /*根据id查询卫星编号*/
        List<List<String>> nodeCodeArray = nodeInfoService.getNodeCodeArray(dataDto.getNodeId().toString());
        if(nodeCodeArray == null ||  nodeCodeArray.isEmpty()){
            return CheckResult.fail("卫星数据为空");
        }
        /*处理数据*/
        List<String> listOneArr = new ArrayList<>();
        nodeCodeArray.forEach(listOneArr::addAll);
//        if(listOneArr.size() != 60 && listOneArr.size() != 108){
//            return CheckResult.fail("只支持60 或108颗卫星");
//        }
        /*替换py文件内容*/
        List<String> strings = FileUtil.readLines(new File(configCalPath.getTemplateFiles()+"parameters.py"), CharsetUtil.UTF_8);
        AtomicInteger i = new AtomicInteger();
        strings.forEach(key ->{
            if(key.startsWith("orbit")){
                strings.set(i.get(),"orbit = "+nodeCodeArray.toString());
            }
            if(key.startsWith("name_real_sa_l")){
                strings.set(i.get(),"name_real_sa_l = "+listOneArr.toString());
            }
            i.getAndIncrement();
        });
        /*获取系统时间*/
        String time = DateUtil.format(new Date(),"yyyyMMddHHmmss");
        FileUtil.writeLines(strings, new File(configCalPath.getTemplateFiles() + "parameters_"+time+".py"), CharsetUtil.UTF_8);
        /*python 文件上传*/
        String pyFileMsg = gg.requestUploadFile("/temp",configCalPath.getTemplateFiles()+"parameters_"+time+".py");
        if(pyFileMsg == null){
            return CheckResult.fail(configCalPath.getTemplateFiles()+"parameters_"+time+".py文件上传失败");
        }
        /*构建对象*/
        GenerateTheoryDataVo build = GenerateTheoryDataVo.builder()
                .groundFile(groundFileMsg)
                .tlevFile(tleFileMsg)
                .pythonFile(pyFileMsg)
                .dateTime(dataDto.getTime())
                .build();
        /*异步上传文件*/
        gg.generateTheoryData(build);
        return CheckResult.success("请求成功，正在异步调用第三方接口，等待反馈");
    }




    @Override
    public CheckResult<Object> getTheoryData(String region,Long nodeId) {
        boolean b1 = checkNodeNumber(nodeId);
        if(!b1){
            return CheckResult.success(new ArrayList<>());
        }
        /*获取token*/
        String adminToken = gg.getAdminToken().get("token").toString();
        if(adminToken == null){
            return CheckResult.fail("获取adminToken失败");
        }
        try {
            /*查询拓扑图列表*/
            HttpResponse execute = HttpRequest.get(configCalPath.getReUrl() + "/jeecg-boot/topology/list?isRelationLink=true")
                    .header("X-Access-Token", adminToken)
                    .execute();
            /*处理结果数据*/
            OutInterfaceServiceImpl.addXZLogs("获取第三方星座拓扑数据："+configCalPath.getReUrl() + "/jeecg-boot/topology/list?isRelationLink=true");
            TopoList jsonRootBean = JSONObject.parseObject(execute.body(), TopoList.class);
            if(HttpStatus.HTTP_OK != jsonRootBean.getCode()){
               return CheckResult.fail("获取远程拓扑数据列表失败");
            }
            List<TopoList.Records> recordsList = jsonRootBean.getResult().getRecords();
            if(recordsList != null && !recordsList.isEmpty()){
                TopoList.Records records = recordsList.get(recordsList.size() - 1);
                /*根据结果id获取卫星数据和链路数据*/
                int id = records.getId();
                /*查询拓扑图列表*/
                /*
                * region = 1 卫星vs卫星
                * region = 2 卫星vs地面站
                * region = 3 地面站vs卫星
                * */
                HttpResponse data = HttpRequest.get(configCalPath.getReUrl() + "/jeecg-boot/topology/queryRelationShips?region="+region+"&topologyId="+id)
                        .header("X-Access-Token", adminToken)
                        .execute();
                /*处理结果数据*/
                SateAndLinkData sateAndLinkData = JSONObject.parseObject(data.body(), SateAndLinkData.class);
                List<SateAndLinkData.Records> records1 = sateAndLinkData.getResult().getRecords();
                /*获取卫星列表*/
                SateListData satelliteList = getSatelliteList();
                if(satelliteList == null){
                    return CheckResult.fail("获取卫星列表失败");
                }
                List<SateListData.Records> sateList = satelliteList.getResult().getRecords();
                /*保持返回给前端的数据格式和评估数据一致*/
                List<SateLinkData> list = new ArrayList<>();
                records1.forEach(item ->{
                    SateLinkData sateLinkData = SateLinkData.builder()
                            .sateRateRealityValue(Double.parseDouble(item.getValue()))
                            .sateRateTheoryValue(Double.parseDouble(item.getValue()))
                            .build();
                    if("1".equals(region)){
                        SateListData.Records recordsOne = sateList.stream().filter(f -> f.getNumber().equals(item.getFirName())).collect(Collectors.toList()).stream().findFirst().orElse(new SateListData.Records());
                        SateListData.Records recordsTwo = sateList.stream().filter(f -> f.getNumber().equals(item.getTwoName())).collect(Collectors.toList()).stream().findFirst().orElse(new SateListData.Records());
                        sateLinkData.setSatelliteCode1(item.getFirName());
                        sateLinkData.setSatelliteCode2(item.getTwoName());
                        sateLinkData.setSatelliteName1(recordsOne.getName());
                        sateLinkData.setSatelliteName2(recordsTwo.getName());
                    }else if("2".equals(region)){
                        /*卫星*/
                        SateListData.Records recordsOne = sateList.stream().filter(f -> f.getNumber().equals(item.getFirName())).collect(Collectors.toList()).stream().findFirst().orElse(new SateListData.Records());
                        sateLinkData.setSatelliteCode1(item.getFirName());
                        sateLinkData.setSatelliteName1(recordsOne.getName());
                        /*地面站*/
                        sateLinkData.setSatelliteCode2(item.getTwoObj().getNumber());
                        sateLinkData.setSatelliteName2(item.getTwoObj().getName());
                    }else if("3".equals(region)){
                        /*地面站*/
                        sateLinkData.setSatelliteCode1(item.getFirObj().getNumber());
                        sateLinkData.setSatelliteName1(item.getFirObj().getName());
                        /*卫星*/
                        SateListData.Records recordsOne = sateList.stream().filter(f -> f.getNumber().equals(item.getTwoName())).collect(Collectors.toList()).stream().findFirst().orElse(new SateListData.Records());
                        sateLinkData.setSatelliteCode2(item.getTwoName());
                        sateLinkData.setSatelliteName2(recordsOne.getName());
                    }
                    list.add(sateLinkData);
                });
                return CheckResult.success(list);
            }
        } catch (Exception e) {
            log.error("获取卫星数据和链路数据失败：{}",e.getMessage());
        }
        return CheckResult.fail("获取卫星数据和链路数据失败");

    }

    @Override
    public SateListData getSatelliteList() {
        /*获取token*/
        String adminToken = gg.getAdminToken().get("token").toString();
        if(adminToken == null){
            return null;
        }
        /*获取卫星列表*/
        HttpResponse data = HttpRequest.get(configCalPath.getReUrl() + "/jeecg-boot/satellite/list?pageNo=1&pageSize=10000")
                .header("X-Access-Token", adminToken)
                .execute();
        OutInterfaceServiceImpl.addXZLogs("获取第三方卫星列表："+configCalPath.getReUrl() + "/jeecg-boot/satellite/list");
        return JSONObject.parseObject(data.body(), SateListData.class);
    }

    @SuppressWarnings("unchecked")
    @Override
    public CheckResult<Object> siteRateAndCache(Long nodeId) {
        boolean b1 = checkNodeNumber(nodeId);
        if(!b1){
            return CheckResult.success(new ArrayList<>());
        }
        /*获取卫星拓扑图*/
        List<SateLinkData> records = (List<SateLinkData>)getTheoryData("1",nodeId).body().getData();
        if(records == null){
            return CheckResult.fail("获取第三方数据为空");
        }
        /*根据firName 排序*/
        /*星间链路速率要取2个卫星之间上下左右激光头速率的最小值*/
        records.forEach(item -> {
            /*获取相连两个卫星*/
            List<SateLinkData> collect = records.stream().filter(f -> f.getSatelliteCode1().equals(item.getSatelliteCode2()) && item.getSatelliteCode2().equals(f.getSatelliteCode1())).collect(Collectors.toList());
            /*最小速率*/
            double minRate = item.getSateRateRealityValue();
            /*获取两个卫星之间速率数据小的数据。如果没有相连接的卫星则取自己的速率*/
            if(!collect.isEmpty()){
                SateLinkData records1 = collect.get(0);
                if(records1.getSateRateRealityValue() < minRate){
                    item.setSateRateRealityValue(records1.getSateRateRealityValue());
                }
            }
        });
        /*处理数据格式，求出每个卫星的速率值：卫星的整体速率为4个激光头发射或者接收速率之和*/
        Set<String> nodeSet = records.stream().map(SateLinkData::getSatelliteCode1).collect(Collectors.toSet());
        List<SateRateAndCacheVo> list = new ArrayList<>();
        /*获取卫星列表*/
        SateListData satelliteList = getSatelliteList();
        List<SateListData.Records> sateList = satelliteList.getResult().getRecords();
        nodeSet.forEach(item -> {
            List<SateLinkData> collect = records.stream().filter(f -> f.getSatelliteCode1().equals(item)).collect(Collectors.toList());
            if(!collect.isEmpty()){
                /*求速率之和*/
                AtomicReference<Double> sum = new AtomicReference<>(0D);
                collect.forEach(val -> sum.updateAndGet(v -> v + val.getSateRateRealityValue()));
                SateLinkData records1 = collect.get(0);
                SateRateAndCacheVo siteRateAndCacheVo = SateRateAndCacheVo.builder()
                        .sateCode(records1.getSatelliteCode1())
                        .sateRateRealityValue(sum.get())
                        .build();
                /*获取卫星名称*/
                List<SateListData.Records> sateListFilter = sateList.stream().filter(sate -> sate.getNumber().equals(item)).collect(Collectors.toList());
                if(!sateListFilter.isEmpty()){
                    siteRateAndCacheVo.setSateName(sateListFilter.get(0).getName());
                    siteRateAndCacheVo.setSateRateTheoryValue(Double.parseDouble(String.valueOf(sateListFilter.get(0).getTranPw())));
                    siteRateAndCacheVo.setCacheRealityValue(2000D);
                    siteRateAndCacheVo.setCacheTheoryValue(Double.parseDouble(String.valueOf(sateListFilter.get(0).getBuffer())));
                }
                list.add(siteRateAndCacheVo);
            }
        });
        /*排序*/
        list.sort((a,b) ->{
            int bInt = Integer.parseInt(b.getSateName().replaceAll("[a-zA-Z]", ""));
            int aInt = Integer.parseInt(a.getSateName().replaceAll("[a-zA-Z]", ""));
            return aInt - bInt;
        });
        return CheckResult.success(list);
    }

    @Override
    public boolean checkNodeNumber(Long nodeId) {
        if(nodeId == null){
            return false;
        }
        /*根据id查询卫星编号*/
        List<List<String>> nodeCodeArray = nodeInfoService.getNodeCodeArray(nodeId.toString());
        /*处理数据*/
        List<String> listOneArr = new ArrayList<>();
        nodeCodeArray.forEach(listOneArr::addAll);
        return listOneArr.size() == 60 || listOneArr.size() == 108;
    }



}
